﻿// <copyright file="RandomPartitionEnumerator.cs" company="Ecole Normale Supérieure">
// Released under GPLv2
// </copyright>
// <summary>Contains the RandomPartitionEnumerator class that enumerates partitions in a somewhat random way.</summary>

namespace Sqwarea.Batch
{
    using System;
    using System.Collections;
    using System.Collections.Generic;
    using System.Drawing;
    using Sqwarea.Persistence;
    using Sqwarea.Persistence.Azure;

    /// <summary>
    /// This class enumerates all partitions with a random start point
    /// </summary>
    public class RandomPartitionEnumerator : IEnumerable<IEnumerable<Point>>
    {
        /// <summary>
        /// GameProvider used by the class, initialized in the construction function
        /// </summary>
        private readonly IGameProvider _gameProvider;

        /// <summary>
        /// GlobalVariables used by the class, initialized in the construction function
        /// </summary>
        private readonly IGlobalVariables _globalVariables;

        /// <summary>
        /// Random source
        /// </summary>
        private readonly Random _random = new Random();

        /// <summary>
        /// Initializes a new instance of the RandomPartitionEnumerator class
        /// </summary>
        /// <param name="gameProvider">The GameProvider to use</param>
        /// <param name="globalVariables">The GlobalVariables to use</param>
        public RandomPartitionEnumerator(IGameProvider gameProvider, IGlobalVariables globalVariables)
        {
            if (gameProvider == null)
            {
                throw new ArgumentNullException("gameProvider");
            }

            this._gameProvider = gameProvider;

            if (globalVariables == null)
            {
                throw new ArgumentNullException("globalVariables");
            }

            this._globalVariables = globalVariables;
        }

        /// <summary>
        /// Enumerates all partitions with a random start point and a fixed circular order(x decreasing, y increasing, then wrapped back)
        /// </summary>
        /// <returns>An IEnumerator of partitions</returns>
        public IEnumerator<IEnumerable<Point>> GetEnumerator()
        {
            double angle = this._random.NextDouble() * 2 * Math.PI;
            double radius = ((double)this._globalVariables.NewKingsRadius) * this._random.NextDouble();
            int startx = (int)Math.Truncate(Math.Cos(angle) * radius / GameProvider.SizeOfSubZones) * GameProvider.SizeOfSubZones;
            int starty = (int)Math.Truncate(Math.Sin(angle) * radius / GameProvider.SizeOfSubZones) * GameProvider.SizeOfSubZones;
            int x = startx;
            int y = starty;
            int upperBound = (int)Math.Truncate((radius * radius) - (x * x));
            do
            {
                yield return new PointEnumerable(x, y);
                y++;
                if (y > upperBound)
                {
                    x--;
                    if (x < -(int)radius)
                    {
                        x = (int)Math.Truncate(radius / GameProvider.SizeOfSubZones) * GameProvider.SizeOfSubZones;
                    }

                    upperBound = (int)Math.Truncate((radius * radius) - (x * x));
                    y = -upperBound;
                }
            }
            while ((startx != x) || (starty != y));
        }

        /// <summary>
        /// Enumerates all partitions with a random start point
        /// </summary>
        /// <returns>An IEnumerator of partitions</returns>
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }

        /// <summary>
        /// This class enumerates points from a partition.
        /// </summary>
        private class PointEnumerable : IEnumerable<Point>
        {
            /// <summary>
            /// Abscisse of the starting point
            /// </summary>
            private readonly int _startx;

            /// <summary>
            /// Coordinate of the starting point
            /// </summary>
            private readonly int _starty;

            /// <summary>
            /// Initializes a new instance of the PointEnumerable class
            /// </summary>
            /// <param name="startx">Abscisse to start</param>
            /// <param name="starty">Coordinate to start</param>
            public PointEnumerable(int startx, int starty)
            {
                this._startx = startx;
                this._starty = starty;
            }

            /// <summary>
            /// Enumerates all points of a partition
            /// </summary>
            /// <returns>An IEnumeartor of points</returns>
            public IEnumerator<Point> GetEnumerator()
            {
                for (int i = 0; i < GameProvider.SizeOfSubZones; i++)
                {
                    for (int j = 0; j < GameProvider.SizeOfSubZones; j++)
                    {
                        yield return new Point(i + this._startx, j + this._starty);
                    }
                }
            }

            /// <summary>
            /// Enumerates all points of a partition
            /// </summary>
            /// <returns>An IEnumeartor of points</returns>
            IEnumerator IEnumerable.GetEnumerator()
            {
                return GetEnumerator();
            }
        }
    }
}
